package com.meida.module.pay.provider.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.egzosn.pay.common.bean.MethodType;
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.common.http.UriVariables;
import com.meida.common.enums.OrderStatusEnum;
import com.meida.common.enums.PayStatusEnum;
import com.meida.common.handler.PayInfoHandler;
import com.meida.common.mybatis.base.service.impl.BaseServiceImpl;
import com.meida.common.mybatis.entity.EntityMap;
import com.meida.common.mybatis.model.GetModel;
import com.meida.common.mybatis.model.PageModel;
import com.meida.common.mybatis.model.ResultBody;
import com.meida.common.mybatis.query.CriteriaQuery;
import com.meida.common.mybatis.query.CriteriaSave;
import com.meida.common.security.OpenHelper;
import com.meida.common.utils.*;
import com.meida.module.pay.client.entity.PayInfo;
import com.meida.module.pay.client.enums.PayType;
import com.meida.module.pay.provider.mapper.PayInfoMapper;
import com.meida.module.pay.provider.response.PayResponse;
import com.meida.module.pay.provider.service.PayConfigService;
import com.meida.module.pay.provider.service.PayInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Map;
import java.util.Optional;

/**
 * 支付信息接口实现类
 *
 * @author flyme
 * @date 2019-06-23
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class PayInfoServiceImpl extends BaseServiceImpl<PayInfoMapper, PayInfo> implements PayInfoService {

    @Autowired
    private PayConfigService payConfigService;
    @Autowired
    private OrderNoUtil orderNoUtil;



    @Override
    public ResultBody createPayInfo(Map params) {
        CriteriaSave cs = new CriteriaSave(params, PayInfo.class);
        Long companyId = OpenHelper.getCompanyId();
        //订单号
        String outTradeNo = Optional.ofNullable((String) params.get("outTradeNo")).orElse(orderNoUtil.getOrderNo());
        ResultBody resultBody = redisUtils.getOrder(outTradeNo);
        if (FlymeUtils.isEmpty(resultBody)) {
            //支付状态
            cs.put("payStatus", PayStatusEnum.NOPAY.code);
            //订单状态
            cs.put("orderStatus", OrderStatusEnum.UNPAY.code);
            cs.put("companyId", companyId);
            cs.put("userId", OpenHelper.getUserId());
            //订单号
            cs.put("outTradeNo", outTradeNo);
            resultBody = savePayInfo(cs, outTradeNo);
            resultBody.setExtra(cs.getRequestMap());
            if (resultBody.isOk()) {
                redisUtils.set("ORDER_" + outTradeNo, resultBody, 1800);
                resultBody.setMsg("订单创建成功");
            }
        }
        return resultBody;
    }


    /**
     * 保存支付信息到数据库
     */
    private ResultBody savePayInfo(CriteriaSave cs, String outTradeNo) {
        //获取订单信息自定义handler
        PayInfoHandler orderHandler = SpringContextHolder.getHandler(cs.getHandlerName(), PayInfoHandler.class);
        ResultBody resultBody = new ResultBody();
        //是否支持扩展条件
        boolean hasHandler = false;
        Boolean isSave = false;
        //保存对象
        PayInfo payInfo = null;
        if (ObjectUtils.isNotNull(orderHandler)) {
            resultBody = orderHandler.validate(cs, cs.getRequestMap());
            if (resultBody.isOk() == false) {
                return resultBody;
            }
            orderHandler.prepare(cs, cs.getRequestMap());
            //获取自定义实体
            payInfo = orderHandler.getPayInfo(cs, cs.getRequestMap(), outTradeNo);
            if (ObjectUtils.isNotEmpty(payInfo)) {
                if (cs.getSaveToRedis() == true) {
                    //保存至redis时isSave要设置为true来执行complete事件
                    isSave = true;
                } else {
                    //自定义mapper
                    BaseMapper mapper = orderHandler.getCustomMapper();
                    if (ObjectUtils.isNotEmpty(mapper)) {
                        //使用自定义mapper保存
                        isSave = mapper.insert(payInfo) > 0;
                    } else {
                        //保存原始对象
                        isSave = saveOrUpdate(payInfo);
                    }
                }
            }
            hasHandler = true;
        }
        //当handler.getEntity对象为空时执行默认保存
        if (!isSave && FlymeUtils.isEmpty(payInfo)) {
            //保存原始对象
            payInfo = (PayInfo) JsonUtils.jsonToBean(JSONUtil.toJsonStr(cs.getRequestMap()), cs.getCls());
            isSave = saveOrUpdate(payInfo);
        }

        if (isSave) {
            if (hasHandler) {
                //保存成功后调用扩展事件
                orderHandler.complete(cs, cs.getRequestMap(), payInfo);
            }
            resultBody.data(payInfo);
        }
        return resultBody.setMsg("保存成功");

    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public PayInfo findByOutTradeNo(String outTradeNo) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(PayInfo.class);
        return getOne(cq.eq(true, "outTradeNo", outTradeNo), false);
    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public PayInfo findNoPayOrder(Class cls, Long companyId) {
        CriteriaQuery<PayInfo> cq = new CriteriaQuery(PayInfo.class);
        return getOne(cq.eq(true, "orderEntity", cls.getSimpleName()).eq(true, "companyId", companyId).eq("payStatus", PayStatusEnum.NOPAY.code), false);
    }


    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody pageList(PageModel pageModel) {
        CriteriaQuery<EntityMap> cq = new CriteriaQuery(pageModel, PayInfo.class);
        cq.select(PayInfo.class, "orderId", "busId", "orderNo", "orderEntity", "orderTitle", "companyId", "orderBody", "payStatus", "orderAmount", "invoiceId", "outTradeNo", "orderStatus");
        Integer invoiceState = cq.getRequestMap().getInt("invoiceState", 0);
        if (FlymeUtils.isNotEmpty(invoiceState)) {
            if (invoiceState.equals(1)) {
                cq.isNotNull("invoiceId");
            } else {
                cq.isNull("invoiceId");
            }
        }
        cq.eq(PayInfo.class, "orderNo");
        cq.eq(PayInfo.class, "orderStatus");
        cq.eq(PayInfo.class, "orderBody");
        cq.eq(PayInfo.class, "payStatus");
        return basePageList(cq);
    }

    /**
     * @param orderId
     * @param invoiceId
     * @return
     */
    @Override
    public Boolean updateInvoiceId(Long orderId, Long invoiceId) {
        UpdateWrapper uw = new UpdateWrapper();
        uw.set(true, "invoiceId", invoiceId);
        uw.eq("orderId", orderId);
        return update(uw);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public ResultBody checkPayState(GetModel model) {
        CriteriaQuery cq = new CriteriaQuery(model, PayInfo.class);
        cq.select(PayInfo.class, "*");
        return baseGet(cq);
    }

    @Override
    public Boolean clearInvoiceId(Long[] invoiceIds) {
        UpdateWrapper uw = new UpdateWrapper();
        uw.set(true, "invoiceId", null);
        uw.in("invoiceId", invoiceIds);
        return update(uw);
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public Boolean validate(String outTradeNo) {
        System.out.println("支付回调" + outTradeNo);
        PayInfo payInfo = findByOutTradeNo(outTradeNo);
        Integer payStatusEnum = payInfo.getPayStatus();
        if (PayStatusEnum.NOPAY.code.equals(payStatusEnum)) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    public EntityMap getOrderParams(String outTradeNo) {
        ResultBody resultBody = redisUtils.getOrder(outTradeNo);
        return Optional.ofNullable(resultBody).orElse(new ResultBody()).getExtra();
    }

    @Override
    public Boolean success(String outTradeNo, Integer orderState) {
        UpdateWrapper updateWrapper = new UpdateWrapper();
        updateWrapper.set(true, "payStatus", PayStatusEnum.PAY.code);
        updateWrapper.set(true, "payDate", DateUtils.getDateTime());
        updateWrapper.set(true, "orderStatus", orderState);
        updateWrapper.eq(true, "outTradeNo", outTradeNo);
        Boolean tag = update(updateWrapper);
        if (tag) {
            //删除缓存订单
            redisUtils.del("ORDER_" + outTradeNo);
        }
        return tag;
    }


    @Override
    public ResultBody getPayInfo(Map params, String tradeType) {
        Object payId = params.get("payId");
        ApiAssert.isNotEmpty("请选择支付方式", payId);
        Object outTradeNo = params.get("outTradeNo");
        ApiAssert.isNotEmpty("订单号不能为空", outTradeNo);
        Object handlerName = params.get("handlerName");
        ApiAssert.isNotEmpty("支付handler不能为空", handlerName);
        PayResponse payResponse = payConfigService.getPayResponse(Long.parseLong(payId.toString()), handlerName.toString());
        ResultBody resultBody = createPayInfo(params);
        if (resultBody.isOk()) {
            PayInfo orderMap = (PayInfo) resultBody.getData();
            String title = orderMap.getOrderTitle();
            String orderBody = orderMap.getOrderBody();
            BigDecimal orderAmount = orderMap.getOrderAmount();
            ApiAssert.isNotEmpty("订单金额不能为空", orderAmount);
            String payType = payResponse.getStorage().getPayType();
            PayOrder payOrder = new PayOrder(title, orderBody, orderAmount, outTradeNo.toString());
            EntityMap extra = new EntityMap();
            Object payInfo = null;
            //APP支付
            if (tradeType.equals("APP")) {
                extra.put("outTradeNo", outTradeNo);
                extra.put("payId", payId);
                payOrder.setTransactionType(PayType.valueOf(payType).getTransactionType(tradeType));
                if (payType.equals(PayType.aliPay.name())) {
                    payInfo = UriVariables.getMapToParameters(payResponse.getService().orderInfo(payOrder));
                }
                if (payType.equals(PayType.wxPay.name())) {
                    Map<String, Object> map = payResponse.getService().orderInfo(payOrder);
                    payInfo = JSONUtil.toJsonStr(map);
                }
            }
            //二维码支付
            if (tradeType.equals("NATIVE")) {
                if (payType.equals(PayType.aliPay.name())) {
                    payOrder.setTransactionType(PayType.valueOf(payType).getTransactionType("SWEEPPAY"));
                }
                if (payType.equals(PayType.wxPay.name())) {
                    payOrder.setTransactionType(PayType.valueOf(payType).getTransactionType("NATIVE"));
                }
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                try {
                    ImageIO.write(payResponse.getService().genQrPay(payOrder), "JPEG", outputStream);
                    payInfo = outputStream.toByteArray();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //支付宝网页支付
            if (tradeType.equals("PAGE")) {
                Map<String, Object> orderInfo = payResponse.getService().orderInfo(payOrder);
                payInfo = payResponse.getService().buildRequest(orderInfo, MethodType.POST);

            }
            return ResultBody.ok("下单成功", payInfo).setExtra(extra);

        } else {
            return resultBody;
        }


    }

}
